{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 面向对象编程的类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  1 模块的使用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将代码分成模块：\n",
    "- 提高可维护性、编写容易，利于拓展；\n",
    "\n",
    "- 避免函数名和变量名冲突；\n",
    "\n",
    "如果模块名冲突，则使用包(Package)来组织模块，只有包名称不冲突就行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.包与模块的组织结构: \n",
      "mycompany\n",
      "   |__web\n",
      "   |   |__ __init__.py\n",
      "   |   |__ utils.py\n",
      "   |   |__ www.py\n",
      "   |__ __init__.py\n",
      "   |__ abc.py\n",
      "   |__ xyz.py\n",
      "   \n",
      "\n"
     ]
    }
   ],
   "source": [
    "string=\"\"\"\n",
    "mycompany\n",
    "   |__web\n",
    "   |   |__ __init__.py\n",
    "   |   |__ utils.py\n",
    "   |   |__ www.py\n",
    "   |__ __init__.py\n",
    "   |__ abc.py\n",
    "   |__ xyz.py\n",
    "   \n",
    "\"\"\"\n",
    "print('1.包与模块的组织结构:',string)\n",
    "# 在包中放模块的位置下一定要有 __init__.py文件，该文件系统是区分包目录和普通目录的标志，该文件可以为空\n",
    "# www.py模块名称为mycompany.web.www, abc.py模块名称为mycompany.abc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结：\n",
    "- 模块名要符号Python变量命名规范\n",
    "- 自定义的模块名不要与系统模块名冲突，定义函数也是，import module_name来查看是否有该模块"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 编写标准模块"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用自定义的Hello.py模块\n",
      "Hello, world!\n",
      "sys.argv: ['Hello.py']\n",
      "-----------------------------\n",
      "Hello, David\n",
      "sys.argv: ['Hello.py', 'David']\n",
      "-----------------------------\n",
      "Too many arguments!\n",
      "sys.argv: ['D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\ipykernel_launcher.py', '-f', 'C:\\\\Users\\\\David\\\\AppData\\\\Roaming\\\\jupyter\\\\runtime\\\\kernel-c590eb6d-74f0-4847-8d1b-d2ff740e7ed1.json']\n"
     ]
    }
   ],
   "source": [
    "# 调用自定义的Hello.py模块，使用命令行之间执行模块脚本\n",
    "print('1.调用自定义的Hello.py模块')\n",
    "%run Hello.py\n",
    "print('-----------------------------')\n",
    "!python Hello.py David\n",
    "print('-----------------------------')\n",
    "# 导入自定义模块\n",
    "import Hello\n",
    "Hello.hello()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "jupyter notebook内容补充:\n",
    "- 单百分号%开头，作用于本行\n",
    "- 双百分号%%开头，作用于本元胞\n",
    "- %run python_file.py执行py文件\n",
    "- !python python_file.py调用命令行执行py文件\n",
    "- %%time显示当前元胞运行耗时\n",
    "- ?sum()可以查看该函数的文档帮助，如果在变量前使用?也会输出该变量信息，ESC或q退出\n",
    "- 两个$$之间插入公式(Markdown状态下)，这是行间公式，使用单个$是行内公式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/json": {
       "cell": {
        "!": "OSMagics",
        "HTML": "Other",
        "SVG": "Other",
        "bash": "Other",
        "capture": "ExecutionMagics",
        "cmd": "Other",
        "debug": "ExecutionMagics",
        "file": "Other",
        "html": "DisplayMagics",
        "javascript": "DisplayMagics",
        "js": "DisplayMagics",
        "latex": "DisplayMagics",
        "markdown": "DisplayMagics",
        "perl": "Other",
        "prun": "ExecutionMagics",
        "pypy": "Other",
        "python": "Other",
        "python2": "Other",
        "python3": "Other",
        "ruby": "Other",
        "script": "ScriptMagics",
        "sh": "Other",
        "svg": "DisplayMagics",
        "sx": "OSMagics",
        "system": "OSMagics",
        "time": "ExecutionMagics",
        "timeit": "ExecutionMagics",
        "writefile": "OSMagics"
       },
       "line": {
        "alias": "OSMagics",
        "alias_magic": "BasicMagics",
        "autocall": "AutoMagics",
        "automagic": "AutoMagics",
        "autosave": "KernelMagics",
        "bookmark": "OSMagics",
        "cd": "OSMagics",
        "clear": "KernelMagics",
        "cls": "KernelMagics",
        "colors": "BasicMagics",
        "config": "ConfigMagics",
        "connect_info": "KernelMagics",
        "copy": "Other",
        "ddir": "Other",
        "debug": "ExecutionMagics",
        "dhist": "OSMagics",
        "dirs": "OSMagics",
        "doctest_mode": "BasicMagics",
        "echo": "Other",
        "ed": "Other",
        "edit": "KernelMagics",
        "env": "OSMagics",
        "gui": "BasicMagics",
        "hist": "Other",
        "history": "HistoryMagics",
        "killbgscripts": "ScriptMagics",
        "ldir": "Other",
        "less": "KernelMagics",
        "load": "CodeMagics",
        "load_ext": "ExtensionMagics",
        "loadpy": "CodeMagics",
        "logoff": "LoggingMagics",
        "logon": "LoggingMagics",
        "logstart": "LoggingMagics",
        "logstate": "LoggingMagics",
        "logstop": "LoggingMagics",
        "ls": "Other",
        "lsmagic": "BasicMagics",
        "macro": "ExecutionMagics",
        "magic": "BasicMagics",
        "matplotlib": "PylabMagics",
        "mkdir": "Other",
        "more": "KernelMagics",
        "notebook": "BasicMagics",
        "page": "BasicMagics",
        "pastebin": "CodeMagics",
        "pdb": "ExecutionMagics",
        "pdef": "NamespaceMagics",
        "pdoc": "NamespaceMagics",
        "pfile": "NamespaceMagics",
        "pinfo": "NamespaceMagics",
        "pinfo2": "NamespaceMagics",
        "pip": "BasicMagics",
        "popd": "OSMagics",
        "pprint": "BasicMagics",
        "precision": "BasicMagics",
        "profile": "BasicMagics",
        "prun": "ExecutionMagics",
        "psearch": "NamespaceMagics",
        "psource": "NamespaceMagics",
        "pushd": "OSMagics",
        "pwd": "OSMagics",
        "pycat": "OSMagics",
        "pylab": "PylabMagics",
        "qtconsole": "KernelMagics",
        "quickref": "BasicMagics",
        "recall": "HistoryMagics",
        "rehashx": "OSMagics",
        "reload_ext": "ExtensionMagics",
        "ren": "Other",
        "rep": "Other",
        "rerun": "HistoryMagics",
        "reset": "NamespaceMagics",
        "reset_selective": "NamespaceMagics",
        "rmdir": "Other",
        "run": "ExecutionMagics",
        "save": "CodeMagics",
        "sc": "OSMagics",
        "set_env": "OSMagics",
        "store": "StoreMagics",
        "sx": "OSMagics",
        "system": "OSMagics",
        "tb": "ExecutionMagics",
        "time": "ExecutionMagics",
        "timeit": "ExecutionMagics",
        "unalias": "OSMagics",
        "unload_ext": "ExtensionMagics",
        "who": "NamespaceMagics",
        "who_ls": "NamespaceMagics",
        "whos": "NamespaceMagics",
        "xdel": "NamespaceMagics",
        "xmode": "BasicMagics"
       }
      },
      "text/plain": [
       "Available line magics:\n",
       "%alias  %alias_magic  %autocall  %automagic  %autosave  %bookmark  %cd  %clear  %cls  %colors  %config  %connect_info  %copy  %ddir  %debug  %dhist  %dirs  %doctest_mode  %echo  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %macro  %magic  %matplotlib  %mkdir  %more  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %popd  %pprint  %precision  %profile  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %ren  %rep  %rerun  %reset  %reset_selective  %rmdir  %run  %save  %sc  %set_env  %store  %sx  %system  %tb  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode\n",
       "\n",
       "Available cell magics:\n",
       "%%!  %%HTML  %%SVG  %%bash  %%capture  %%cmd  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%markdown  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile\n",
       "\n",
       "Automagic is ON, % prefix IS NOT needed for line magics."
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%lsmagic  #列出魔法操作符支持的操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "模块的作用域\n",
    "- 使用\\__author\\__、\\__name\\__、\\__doc\\__形式的特殊变量，有特殊用途\n",
    "- _XXX，如_abc这样的变量为私有变量，这与C++的还有所区别，虽然是private的，却能直接引用，但一般不引用\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'This function can output the parameters which is got in the command line.'"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import Hello\n",
    "Hello.hello.__doc__  # 双下划线变量的特殊用途，__doc__变量表示输出文档注释"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用第一种私有函数: Hi, Li\n",
      "2.调用第二种私有函数: Hello, David\n"
     ]
    }
   ],
   "source": [
    "# 私有函数示例\n",
    "def _private1(name):    # 私有函数：使用名字前单下划线、或双下滑线表示私有函数，一般不直接外部调用\n",
    "    return 'Hello, %s' % name\n",
    "def __private2(name):\n",
    "    return 'Hi, %s' % name\n",
    "def greeting(name):     # 公有函数：调用私有函数，为外部调用提供接口\n",
    "    if len(name) > 3:\n",
    "        return _private1(name)\n",
    "    else:\n",
    "        return __private2(name)\n",
    "print('1.调用第一种私有函数:',greeting('Li'))\n",
    "print('2.调用第二种私有函数:',greeting('David'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 使用第三方模块"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一般使用pip可以直接安装第三方模块，在[Python官网](https://pypi.org)可以查询并下载模块，\n",
    "`pip install Module_name`安装该模块\n",
    "- 由于国外pip源较慢，所以pip换国内源才能提高下载速度\n",
    "- [Anaconda](https://www.anaconda.com/download/)已经集成了大部分常用的包，很方便"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['', 'D:\\\\ProgramData\\\\Anaconda3\\\\python36.zip', 'D:\\\\ProgramData\\\\Anaconda3\\\\DLLs', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib', 'D:\\\\ProgramData\\\\Anaconda3', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32\\\\lib', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\Pythonwin', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\IPython\\\\extensions', 'C:\\\\Users\\\\David\\\\.ipython']\n",
      "-----------------------------\n",
      " ['', 'D:\\\\ProgramData\\\\Anaconda3\\\\python36.zip', 'D:\\\\ProgramData\\\\Anaconda3\\\\DLLs', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib', 'D:\\\\ProgramData\\\\Anaconda3', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\win32\\\\lib', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\Pythonwin', 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\IPython\\\\extensions', 'C:\\\\Users\\\\David\\\\.ipython', 'C:\\\\Users\\\\David\\\\Desktop\\\\python基础巩固\\\\python_learning']\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "print(sys.path)   # 该路径是python搜索模块的路径\n",
    "# 将自定义模块路径添加到python的搜索路径，该路径运行结束后失效，主要windows下路径为 \\\\ 分割的\n",
    "# 1.直接在sys.path路径列表后添加自定义模块路径\n",
    "sys.path.append('C:\\\\Users\\\\David\\\\Desktop\\\\python基础巩固\\\\python_learning')\n",
    "print('-----------------------------\\n',sys.path)\n",
    "# 2.在系统的PYTHONPATH中添加自定义模块路径"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2 面向对象编程之类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 面向过程与面向对象区别"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用面向过程的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用面向过程的方法来输出学生成绩:\n",
      "Michael: 98\n",
      "Bob: 97\n"
     ]
    }
   ],
   "source": [
    "# 输出学生成绩\n",
    "std1 = {'name':'Michael', 'score':98}        # 数据单独定义\n",
    "std2 = {'name':'Bob', 'score':97}\n",
    "def print_score(std):                        # 处理数据的函数与数据分离\n",
    "    print('%s: %s' % (std['name'], std['score']))\n",
    "print('1.使用面向过程的方法来输出学生成绩:')\n",
    "print_score(std1)\n",
    "print_score(std2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用面向对象的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用面向对象的方法来输出学生成绩:\n",
      "Michael Jodan: 97\n",
      "Bob Li: 97\n",
      "2.类的结果: <class '__main__.Student'>\n",
      "3.实例后的结果: <__main__.Student object at 0x000002182113BCF8>\n"
     ]
    }
   ],
   "source": [
    "# 输出学生成绩\n",
    "class Student(object):                   # 定义一个Student的类 Class\n",
    "    def __init__(self, name, score):     # 初始化函数，每个类必须，初始化数据任务\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "    def print_score(self):               # 类的方法，处理数据的任务\n",
    "        print('%s: %s' % (self.name, self.score))\n",
    "Michael = Student('Michael Jodan', 97)   # 实例化定义的Student对象 Instance\n",
    "Bob = Student('Bob Li', 97)\n",
    "print('1.使用面向对象的方法来输出学生成绩:')\n",
    "Michael.print_score()                    # 调用实例化对象的类方法 \n",
    "Bob.print_score()\n",
    "print('2.类的结果:', Student)\n",
    "print('3.实例后的结果:', Bob)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结:\n",
    "- 面向过程中数据与数据处理是分开的，而面向对象中数据与数据处理是封装到一起的，所以后者抽象程度更高\n",
    "- 面向对象设计思想是抽象出Class，根据类创建Instance\n",
    "- 面向对象三大特点：数据封装、继承和多态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 类和实例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用面向对象的方法来输出学生成绩:\n",
      "Michael Jodan: 97\n",
      "分数等级: A\n",
      "2.可以外部改变实例的属性:\n",
      "Michael: 100\n",
      "3.可以从外部赋值新的属性: 20\n",
      "4.列出实例的所有属性:\n",
      " ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'get_grade', 'name', 'print_score', 'score']\n",
      "5.判断Michael的age属性是否存在: True\n",
      "6.获取Michael的age属性的值: 20\n",
      "7.重新设置Michael的age属性的值: 27\n",
      "8.Michael实例的属性: {'name': 'Michael', 'score': 100, 'age': 27}\n",
      "9.Michael实例对应的类文档注释: This a class for print the students' score \n"
     ]
    }
   ],
   "source": [
    "class Student(object):                   # object表示没有可继承的父类，如果有则为父类名称\n",
    "    \"\"\"This a class for print the students' score \"\"\"\n",
    "    def __init__(self, name, score):     # self指向创建的实例本身，初始化函数绑定类的属性\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "    def print_score(self):               # 类的方法，处理数据的任务，第一个参数永远是self\n",
    "        print('%s: %s' % (self.name, self.score))\n",
    "    def get_grade(self):                 # 一个类可以有多个方法，并且也可以有私有的方法\n",
    "        if self.score > 90:\n",
    "            return 'A'\n",
    "        elif self.score >= 60:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'C'       \n",
    "Michael = Student('Michael Jodan', 97)   # 实例化定义的Student对象 Instance\n",
    "print('1.使用面向对象的方法来输出学生成绩:')\n",
    "Michael.print_score()                    # 调用实例化对象的类方法 \n",
    "out = Michael.get_grade()\n",
    "print('分数等级:', out)\n",
    "Michael.name = 'Michael'                 # 外部调用属性变量，改变其值\n",
    "Michael.score = 100\n",
    "print('2.可以外部改变实例的属性:')\n",
    "Michael.print_score()\n",
    "# 和静态语言不同，Python允许对实例变量绑定任何数据，也就是说，对于两个实例变量，\n",
    "# 虽然它们都是同一个类的不同实例，但拥有的变量名称都可能不同\n",
    "Michael.age = 20                         # 可以给实例赋值新的属性\n",
    "print('3.可以从外部赋值新的属性:', Michael.age)\n",
    "print('4.列出实例的所有属性:\\n',dir(Michael))                          # 列出实例的所有属性\n",
    "print('5.判断Michael的age属性是否存在:', hasattr(Michael, 'age'))      # 判断实例某个属性是否存在\n",
    "# 获取实例某个属性的值，如果不存在age属性，则返回404\n",
    "print('6.获取Michael的age属性的值:', getattr(Michael, 'age', 404))    # 该函数可以获取属性及方法   \n",
    "setattr(Michael, 'age', 27)              # 设置实例某个属性的值\n",
    "print('7.重新设置Michael的age属性的值:', Michael.age)\n",
    "print('8.Michael实例的属性:', Michael.__dict__)   # 实例在初始化函数中及之后添加的的属性\n",
    "print('9.Michael实例对应的类文档注释:', Michael.__doc__)    # 类的说明文档"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结：\n",
    "- object.new_attribute = attribute_vlaue，在该类实例化后可以添加新的属性"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 访问限制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用面向对象的方法来输出学生成绩: Michael Jodan: 97\n",
      "2.在私有变量前加_Student可以外部访问: 97\n",
      "3.在私有方法前加_Student可以外部访问: Michael Jodan: 97\n",
      "4.使用单下划线定义的函数(或变量)外部仍可以访问: Michael Jodan: 97\n",
      "-------------------------------------------------------------\n",
      "5.使用get_name()方法获取学生姓名: Michael Jodan\n",
      "5.使用get_score()方法获取重新赋值后的分数: 100\n"
     ]
    }
   ],
   "source": [
    "# 在类中有属性和方法，通常定义的属性在实例外部就可以操作，这些属性都是共有的\n",
    "# 使用self.__XXX这样的属性不能被外部访问，称为私有属性\n",
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name  # 名称前加双下划线代表私有属性\n",
    "        self.__score = score\n",
    "    def print_score(self):           \n",
    "        print('%s: %s' % (self.__name, self.__score))\n",
    "    def _print_name(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))    # 但下划线的函数可以外部访问\n",
    "    def __print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))    # 双下划线的函数在名字前加_Student可以访问\n",
    "    \n",
    "    def get_name(self):         # 定义共有方法来访问私有属性，为外部提供接口\n",
    "        return self.__name\n",
    "    def get_score(self):        # 获取分数\n",
    "        return self.__score\n",
    "    def set_score(self, score): # 设置分数，并且加入了判断逻辑\n",
    "        if 0 <= score <= 100:   # 检测数据的合理性\n",
    "            self.__score = score\n",
    "        else:\n",
    "            raise ValueError('invalid score')\n",
    "       \n",
    "Michael = Student('Michael Jodan', 97)   \n",
    "print('1.使用面向对象的方法来输出学生成绩:', end=' ')\n",
    "Michael.print_score() \n",
    "print('2.在私有变量前加_Student可以外部访问:', end=' ')\n",
    "print(Michael._Student__score)\n",
    "print('3.在私有方法前加_Student可以外部访问:', end=' ')\n",
    "Michael._Student__print_score()\n",
    "print('4.使用单下划线定义的函数(或变量)外部仍可以访问:', end= ' ')\n",
    "Michael._print_name()\n",
    "print('-------------------------------------------------------------')\n",
    "out = Michael.get_name()       # 使用公有方法获取实例的私有属性变量\n",
    "print('5.使用get_name()方法获取学生姓名:', out)\n",
    "Michael.set_score(100)         # 使用公有方法设置私有变量的值\n",
    "out = Michael.get_score()\n",
    "print('5.使用get_score()方法获取重新赋值后的分数:', out)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.4 继承和多态"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 继承就是定义新的类从已有的类继承得到，新的类称为子类(Subclass),被继承的类称为基类、父类或超类(Base class, Super class)，子类可以使用父类的所有方法\n",
    "2. 一个实例的数据类型是子类，则它是数据类型也是父类，子类中定义父类中存在的方法会覆盖父类中的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用子类的结果: Animal is running...\n",
      "2.子类中覆盖父类中已有的方法: Cat is running...\n",
      "3.子类中增加新的方法: Cat is eating...\n"
     ]
    }
   ],
   "source": [
    "# 定义父类\n",
    "class Animal(object):       # 定义一个父类Animal，这里因为没有属性数据，所以没有进行初始化，\n",
    "    def run(self):\n",
    "        print('Animal is running...')\n",
    "# 定义子类\n",
    "class Dog(Animal):          # 继承Animal父类，所以括号中是Animal\n",
    "    pass\n",
    "class Cat(Animal):          # 重新定义run方法，这里会覆盖父类中的run方法，可以进行多次继承\n",
    "    def run(self):\n",
    "        print('Cat is running...')\n",
    "    def eat(self):          # 子类中增加父类中没有的方法\n",
    "        print('Cat is eating...')\n",
    "print('1.调用子类的结果:', end=' ')\n",
    "dog = Dog()    # 实例化一个子类\n",
    "dog.run()      # 继承特性，如果子类无此方法，则直接调用父类中的方法\n",
    "print('2.子类中覆盖父类中已有的方法:', end=' ')\n",
    "cat = Cat()    # 调用子类的run方法，由于子类方法已经进行了重定义，所以调用的是子类中的方法\n",
    "cat.run()\n",
    "print('3.子类中增加新的方法:', end=' ')\n",
    "cat.eat()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.子类实例化后的数据类型:\n",
      "Cat: True\n",
      "Animal: True\n"
     ]
    }
   ],
   "source": [
    "# 子类的实例数据类型：子类的类型 or 父类的类型，体现了类的多态性\n",
    "print('1.子类实例化后的数据类型:')\n",
    "print('Cat:', isinstance(cat, Cat))\n",
    "print('Animal:', isinstance(cat, Animal))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "python动态语言的\"鸭子类型\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running...\n",
      "Animal is running...\n",
      "Cat is running...\n",
      "Cat is running...\n",
      "Tortoise is running slowly...\n",
      "Tortoise is running slowly...\n"
     ]
    }
   ],
   "source": [
    "def run_twice(animal):\n",
    "    animal.run()\n",
    "    animal.run()\n",
    "class Tortoise(object):   # !!!这里的新类不是继承后的子类，\n",
    "    def run(self):        # 与Animal及其子类同样的方法，仍可以传入上述函数\n",
    "        print('Tortoise is running slowly...')\n",
    "run_twice(Animal())\n",
    "run_twice(Cat())\n",
    "# 动态语言的“鸭子类型”，它并不要求严格的继承体系，\n",
    "# 一个对象只要“看起来像鸭子，走起路来像鸭子”，那它就可以被看做是鸭子\n",
    "run_twice(Tortoise())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结：\n",
    "- Python的\"file-like object\"就是鸭子类型，只要是实现了read()方法的对象都可以传入，即使不是真的文件对象\n",
    "- 继承将父类的方法给予了子类\n",
    "- 多态丰富了子类方法的多样性，可以覆盖父类中相同的方法，可以增加新方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.5 获取对象信息"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(1) type()函数获取数据类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用type()函数判断对象的数据类型: <class 'str'> <class 'int'> <class 'float'>\n",
      "2.使用types模块判断对象是否是函数:\n",
      "<class 'function'> True\n",
      "<class 'builtin_function_or_method'> True\n",
      "<class 'function'> True\n",
      "<class 'generator'> True\n"
     ]
    }
   ],
   "source": [
    "# 使用type()函数\n",
    "import types   # 该模块可以判断一个对象是否是函数\n",
    "def func():\n",
    "    pass\n",
    "print('1.使用type()函数判断对象的数据类型:', type('123'), type(124), type(2.2))\n",
    "print('2.使用types模块判断对象是否是函数:')\n",
    "print(type(func), type(func) == types.FunctionType)\n",
    "print(type(abs), type(abs) == types.BuiltinFunctionType)\n",
    "print(type(lambda x: x), type(lambda x: x) == types.LambdaType)\n",
    "print(type(x for x in range(10)), type(x for x in range(10)) == types.GeneratorType)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(2) isinstance()函数判断对象是否是某种类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Dog'> True\n",
      "<class '__main__.Animal'> True\n"
     ]
    }
   ],
   "source": [
    "# 1. isinstance()判断类(Class)的类型\n",
    "dog = Dog()         # 实例化一个子类\n",
    "print(type(Dog()), isinstance(dog, Dog))\n",
    "print(type(Animal()), isinstance(dog, Animal))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.str: True\n",
      "2.int: True\n",
      "3.bytes: True\n"
     ]
    }
   ],
   "source": [
    "# 2.isinstance()判断基本的数据类型\n",
    "print('1.str:', isinstance('abc', str))\n",
    "print('2.int:', isinstance(123, int))\n",
    "print('3.bytes:', isinstance(b'abc', bytes))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.str or int: True\n",
      "2.list or tuple: True\n"
     ]
    }
   ],
   "source": [
    "# 3.isinstance()判断是几个类型中的一种\n",
    "print('1.str or int:', isinstance(1, (str,int)))  # 这里1是int型，所以输出为True\n",
    "print('2.list or tuple:', isinstance([1,2,3], (list, tuple)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(3) dir()函数获取对象的属性及方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.使用dir()方法获取类的属性和方法:\n",
      "['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat', 'run']\n",
      "2.使用len()获取字符串的长度: 3\n",
      "3.使用__len__()属性获取字符串长度: 3\n"
     ]
    }
   ],
   "source": [
    "# 获取对象的属性及方法\n",
    "print('1.使用dir()方法获取类的属性和方法:')\n",
    "print(dir(cat))\n",
    "print('2.使用len()获取字符串的长度:',len('abc'))        # len()函数也是调用__len__()属性\n",
    "print('3.使用__len__()属性获取字符串长度:', 'abc'.__len__())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.调用len()函数返回len属性: 777\n",
      "2.调用__len__()属性: 777\n"
     ]
    }
   ],
   "source": [
    "# 调用len()函数本质是调用对象的__len__()属性\n",
    "class Dog(object):\n",
    "    def __len__(self):\n",
    "        return 777\n",
    "dog = Dog()\n",
    "print('1.调用len()函数返回len属性:', len(dog))\n",
    "print('2.调用__len__()属性:', dog.__len__())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.使用属性获取的方式:\n",
      "Please input a method:add\n",
      "Please input two number:3 4\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "# 利用getattr简化代码\n",
    "class MyOperation(object):         # 自定义运算类\n",
    "    def __init__(self, x=1, y=1):      # 需要初始化两个数\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "    def add(self):   # 加法\n",
    "        return round(self.x + self.y, 2)  # 控制小数点后两位小数\n",
    "    def sub(self):\n",
    "        return '%.2f' % (self.x - self.y) # 控制小数点后两位小数\n",
    "    def multiply(self):\n",
    "        return self.x * self.y\n",
    "    def div(self):\n",
    "        return self.x / self.y\n",
    "\n",
    "# 方法一、使用多组判断了实现运算逻辑，缺点：需要逻辑判断太多\n",
    "def run(computer):\n",
    "    inp = input('Please input a method:')\n",
    "    x_str, y_str = input('Please input two number:').split(' ')\n",
    "    x_num, y_num = map(eval, (x_str, y_str))   # 使用map函数将eval将各字符串转换为数\n",
    "    # print(type(x_str), type(x_num))\n",
    "    computer.x = x_num                        # 为x，y赋初值\n",
    "    computer.y = y_num\n",
    "    if inp == 'add':\n",
    "        return computer.add()\n",
    "    elif inp == 'sub':\n",
    "        return computer.sub()\n",
    "    elif inp == 'multiply':\n",
    "        return computer.multiply()\n",
    "    elif inp == 'div':\n",
    "        return computer.div()\n",
    "    else:\n",
    "        print('Wrong operation.')    \n",
    "# print('1.使用多个逻辑判断的方式:')\n",
    "# mycomputer = MyOperation()          # 实例化一个运算类\n",
    "# out = run(mycomputer)\n",
    "# print(out)\n",
    "# 使用属性获取的函数自动判断方法名称\n",
    "def run2(computer):\n",
    "    inp = input('Please input a method:')\n",
    "    x_str, y_str = input('Please input two number:').split(' ')\n",
    "    x_num, y_num = map(eval, (x_str, y_str))   # 使用map函数将eval将各字符串转换为数\n",
    "    # print(type(x_str), type(x_num))\n",
    "    computer.x = x_num                         # 为x，y赋初值\n",
    "    computer.y = y_num\n",
    "    if hasattr(computer, inp):                 # 判断是否有这个属性\n",
    "        func = getattr(computer, inp)          # 获取实例的inp方法(函数)，同样可以获取属性(数值)\n",
    "        return func()                          # 调用已获取的方法\n",
    "    else:\n",
    "        setattr(computer, inp, lambda x, y : x ** y)  # 新增一个方法\n",
    "        func = getattr(computer, inp)            # 将新的方法赋值给func\n",
    "        return func(computer.x, computer.y)\n",
    "print('2.使用属性获取的方式:')\n",
    "mycomputer = MyOperation()          # 实例化一个运算类\n",
    "out = run2(mycomputer)\n",
    "print(out)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.实例的所有属性及方法: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'add', 'div', 'multiply', 'sub', 'x', 'y']\n",
      "2.实例的属性: {'x': 3, 'y': 4}\n"
     ]
    }
   ],
   "source": [
    "print('1.实例的所有属性及方法:',dir(mycomputer))\n",
    "print('2.实例的属性:',mycomputer.__dict__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.6 实例的属性和类属性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*** 0\n",
      "1.类的属性: 1 2 3\n",
      "2.实例的属性: 100\n"
     ]
    }
   ],
   "source": [
    "# 使用类属性count统计类实例化的次数\n",
    "class Student(object):\n",
    "    count = 0\n",
    "    print('***',count)                # 该类在定义后即使没有实例化，运行就会输出该句，有点像全局变量\n",
    "    def __init__(self, name):\n",
    "        Student.count += 1\n",
    "        self.name = name         # self是指向实例的，所以统计必须使用类属性\n",
    "print('1.类的属性:' , end=' ')\n",
    "o1 = Student('a')\n",
    "print(Student.count , end=' ')             # count是类的属性，在多个实例间是共享的\n",
    "o2 = Student('b')\n",
    "print(Student.count , end=' ')\n",
    "o3 = Student('c')\n",
    "print(Student.count)\n",
    "o3.score = 100\n",
    "print('2.实例的属性:',o3.score)             # score是实例的属性，所以只有该实例有该属性 "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小结：\n",
    "- 一个类被实例化，会默认执行一次\\__init\\__()函数，不会执行类本身\n",
    "- 类的属性在所以实例间是共享的"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
